# Kestrel: An XMPP-based Many-Task Computing Scheduler
# Author: Lance Stout <lancestout@gmail.com>
#
# Copyright 2009-2010 Clemson University
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sleekxmpp
import sleekxmpp.componentxmpp

from kestrel.core.xmpp import Base

class Component(Base):
    """
    A component XMPP module. Using this module should typically
    be done in applications with a large number of subscribed agents.

    Configuration entries for this module:
      XMPP/component/username
      XMPP/component/secret
      XMPP/component/server
      XMPP/component/port
      XMPP/type

    Events listened for by this module:
      system/start
      system/shutdown
      message/send
      presence/send

    Events raised by this module:
      system/available
      system/online
      system/offline
      message
      presence/available
      presence/busy
      presence/offline
      presence/subscribe
      presence/subscribed
      presence/unsubscribe
      presence/unsubscribed
    """

    def __init__(self, kernel):
        """
        Instantiate an XMPP client module that will connect
        to the given server using the username and password
        as credentials.
        """
        self.xmpp = None
        self.kernel = kernel

        username = kernel.config('XMPP/component/username')
        self.server = kernel.config('XMPP/component/server')
        self.secret = kernel.config('XMPP/component/secret')
        self.port = kernel.config('XMPP/component/port')
        self.jid = username + '.' + self.server
        self.has_roster = False

        self.events = {'system/start': self.connect,
                       'system/shutdown': self.disconnect,
                       'message/send': self.send_message,
                       'presence/send': self.send_status,
                       'system/roster/loaded': self.connect}

        self.kernel.register(self)

    def connect(self, roster=None):
        """
        Start the XMPP module and establish a connection with the server.
        """
        self.kernel.set_config('XMPP/type', 'component')

        if roster is None:
            roster = self.kernel.config('XMPP/roster', 'roster.tmp')
            self.kernel.event('system/roster/load', roster)
            return

        self.xmpp = sleekxmpp.componentxmpp.ComponentXMPP(self.jid,
                                                          self.secret,
                                                          self.server,
                                                          self.port)
        if roster is not None:
            self.xmpp.roster = roster

        # Since the SleekXMPP library also uses an event engine, we
        # must provide event handlers to it which will in turn raise
        # events for our kernel to process.
        self.xmpp.add_event_handler("session_start", self.on_start)
        self.xmpp.add_event_handler("message", self.recv_message,
                                    threaded=True)
        self.xmpp.add_event_handler("got_online", self.recv_status,
                                    threaded=True)
        self.xmpp.add_event_handler("got_offline", self.recv_status,
                                    threaded=True)
        self.xmpp.add_event_handler("changed_status", self.recv_status,
                                    threaded=True)
        self.xmpp.add_event_handler("changed_subscription", self.change_roster,
                                    threaded=True)
        # SleekXMPP by default authorizes and subscribes to any subscription
        # request. Overriding these settings is needed in order to make
        # subscription behaviour configurable through application policy.
        self.xmpp.auto_authorize = None
        self.xmpp.auto_subscribe = False

        if self.xmpp.connect():
            self.xmpp.process(threaded=False)
        else:
            self.kernel.log("ERROR", "Could not establish XMPP connection.")
